package com.aopcinemas.cinema;

import jade.content.ContentElement;
import jade.content.lang.Codec.CodecException;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.OntologyException;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.Agent;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.MessageTemplate.MatchExpression;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import javax.swing.JOptionPane;

import com.aopcinemas.cinema.behaviour.OrderInfoBehavior;
import com.aopcinemas.cinema.behaviour.ScheduleInfoBehaviour;
import com.aopcinemas.cinema.dao.ClientDao;
import com.aopcinemas.cinema.dao.DBUtil;
import com.aopcinemas.cinema.dao.HallDao;
import com.aopcinemas.cinema.dao.InvoiceDao;
import com.aopcinemas.cinema.dao.MovieDao;
import com.aopcinemas.cinema.dao.ScheduleDao;
import com.aopcinemas.cinema.dao.SessionDao;
import com.aopcinemas.cinema.model.Cinema;
import com.aopcinemas.cinema.ontology.CinemaOntology;
import com.aopcinemas.cinema.ontology.GetScheduleList;
import com.aopcinemas.cinema.ontology.OrderRequest;
import com.aopcinemas.cinema.ui.CinemaFrame;

public class CinemaAgent extends Agent {

    private static final long serialVersionUID = 1L;

    private Cinema cinema;
    private ScheduleDao scheduleDao;
    private ClientDao clientDao;
    private SessionDao sessionDao;
    private InvoiceDao invoiceDao;
    private MovieDao movieDao;
    private HallDao hallDao;
    private SLCodec codec = new SLCodec();

    public Cinema getCinema() {
        return cinema;
    }

    public void setCinema(Cinema cinema) {
        this.cinema = cinema;
    }

    public ScheduleDao getScheduleDao() {
        return scheduleDao;
    }

    public void setScheduleDao(ScheduleDao scheduleDao) {
        this.scheduleDao = scheduleDao;
    }

    public ClientDao getClientDao() {
        return clientDao;
    }

    public void setClientDao(ClientDao clientDao) {
        this.clientDao = clientDao;
    }

    public SessionDao getSessionDao() {
        return sessionDao;
    }

    public void setSessionDao(SessionDao sessionDao) {
        this.sessionDao = sessionDao;
    }

    public InvoiceDao getInvoiceDao() {
        return invoiceDao;
    }

    public void setInvoiceDao(InvoiceDao invoiceDao) {
        this.invoiceDao = invoiceDao;
    }

    public MovieDao getMovieDao() {
        return movieDao;
    }

    public void setMovieDao(MovieDao movieDao) {
        this.movieDao = movieDao;
    }

    public HallDao getHallDao() {
        return hallDao;
    }

    public void setHallDao(HallDao hallDao) {
        this.hallDao = hallDao;
    }

    public SLCodec getCodec() {
        return codec;
    }

    public void setCodec(SLCodec codec) {
        this.codec = codec;
    }

    @Override
    protected void setup() {
        initializeOntology();
        initializeDAO();
        initializeBehaviours();
        initializeUI();
    }

    @Override
    protected void takeDown() {}

    void initializeUI() {
        CinemaFrame cinemaFrame = new CinemaFrame(this);
        cinemaFrame.setVisible(true);
    }

    void initializeBehaviours() {
        MessageTemplate tmp = getScheduleInfoBehaviorTemplate();
        addBehaviour(new ScheduleInfoBehaviour(scheduleDao, this, tmp));

        tmp = getOrderInfoBehaviorTemplate();
        addBehaviour(new OrderInfoBehavior(this, tmp));
    }

    private MessageTemplate getOrderInfoBehaviorTemplate() {
        MessageTemplate perfomativeTmp = MessageTemplate.MatchPerformative(ACLMessage.REQUEST);
        MessageTemplate receiver = MessageTemplate.MatchReceiver(new AID[] { this.getAID() });
        MessageTemplate content = new MessageTemplate(new MatchExpression() {
            private static final long serialVersionUID = 1L;

            @Override
            public boolean match(ACLMessage msg) {
                try {
                    System.out.println("Check campaign!");
                    if (msg.getOntology() != null && !msg.getOntology().toLowerCase().equals(CinemaOntology.NAME.toLowerCase())) {
                        System.out.println("Ontology not ok!");
                        return false;
                    }
                    System.out.println("Ontology ok!");
                    ContentElement element = CinemaAgent.this.getContentManager().extractContent(msg);
                    if (!(element instanceof Action)) {
                        System.out.println("Element is not action!");
                        return false;
                    }
                    Action action = (Action) element;
                    System.out.println("Action : " + (action.getAction() instanceof OrderRequest));
                    return action.getAction() instanceof OrderRequest;
                } catch (CodecException e) {
                    e.printStackTrace();
                } catch (OntologyException e) {
                    e.printStackTrace();
                }
                return false;
            }
        });
        MessageTemplate tmp = MessageTemplate.and(perfomativeTmp, receiver);
        tmp = MessageTemplate.and(content, tmp);
        return tmp;
    }

    private MessageTemplate getScheduleInfoBehaviorTemplate() {
        MessageTemplate perfomativeTmp = MessageTemplate.MatchPerformative(ACLMessage.REQUEST);
        MessageTemplate receiver = MessageTemplate.MatchReceiver(new AID[] { this.getAID() });
        MessageTemplate content = new MessageTemplate(new MatchExpression() {
            private static final long serialVersionUID = 1L;

            @Override
            public boolean match(ACLMessage msg) {
                try {
                    System.out.println("Check campaign!");
                    if (msg.getOntology() != null && !msg.getOntology().toLowerCase().equals(CinemaOntology.NAME.toLowerCase())) {
                        System.out.println("Ontology not ok!");
                        return false;
                    }
                    System.out.println("Ontology ok!");
                    ContentElement element = CinemaAgent.this.getContentManager().extractContent(msg);
                    if (!(element instanceof Action)) {
                        System.out.println("Element is not action!");
                        return false;
                    }
                    Action action = (Action) element;
                    System.out.println("Action : " + (action.getAction() instanceof GetScheduleList));
                    return action.getAction() instanceof GetScheduleList;
                } catch (CodecException e) {
                    e.printStackTrace();
                } catch (OntologyException e) {
                    e.printStackTrace();
                }
                return false;
            }
        });
        MessageTemplate tmp = MessageTemplate.and(perfomativeTmp, receiver);
        tmp = MessageTemplate.and(content, tmp);
        return tmp;
    }

    private void initializeOntology() {
        getContentManager().registerLanguage(codec);
        getContentManager().registerOntology(CinemaOntology.getInstance());
    }

    public void initializeDAO() {
        try {

            String dbname = JOptionPane.showInputDialog("Database name");
            String dbUrl = "jdbc:hsqldb:file:temp/" + dbname + ";shutdown=true";

            Connection connection = DriverManager.getConnection(dbUrl, "SA", "");
            DBUtil.generateDBIfNotExists(connection);
            connection.close();

            connection = DriverManager.getConnection(dbUrl, "SA", "");
            scheduleDao = new ScheduleDao(connection);
            clientDao = new ClientDao(connection);
            sessionDao = new SessionDao(connection);
            invoiceDao = new InvoiceDao(connection);
            movieDao = new MovieDao(connection);
            hallDao = new HallDao(connection);

        } catch (SQLException e) {
            throw new RuntimeException("Can't initialize connection with cinema database", e);
        }
    }
}
